package com.cqsd.ioc.core.bean;

import com.cqsd.bean.BeanUtils;
import com.cqsd.ioc.annotation.Scope;
import com.cqsd.ioc.trait.use.FactoryBean;
import com.cqsd.ioc.util.AnnotationUtil;
import com.cqsd.result.Box;
import org.jetbrains.annotations.Contract;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.*;
import java.util.function.Predicate;

/**
 * @author caseycheng
 * 传入一个扫描到的Bean，并开始解析
 * @date 2023/3/11-10:55
 **/
public class BeanDef implements com.cqsd.ioc.trait.BeanInfo {
    /**
     * 被封装的类型
     */
    private final Class<?> type;
    /**
     * 所有方法
     */
    private final Method[] allMethods;
    /**
     * 所有字段
     */
    private final Field[] allFields;
    /**
     * 所有构造器
     */
    private final Constructor<?>[] allConstructor;
    /**
     * 单例标识
     */
    private final String singleFlag;
    /**
     * 该bean被分配到的名字
     */
    private String name;
    /**
     * 是否需要懒加载
     */
    private boolean laze = false;
    /**
     * 在这里存储bean的引用
     */
    private Object instance = null;
    /**
     * bean构建工厂，优先使用工厂来构建bean对象
     */
    private FactoryBean factory;
    /**
     * cache
     */
    @SuppressWarnings("rawtypes")
    private Map<Predicate, Object[]> cache = null;

    public BeanDef(Class<?> type) {
        this.type = type;
        //解析作用域
        final Scope scope = AnnotationUtil.findAnnotation(type, Scope.class);
        if (scope == null) {
            this.singleFlag = "singleton";
        } else {
            this.singleFlag = scope.value();
        }
        //获取所有构造器
        this.allConstructor = type.getDeclaredConstructors();
        final List<Method> allMethods = BeanUtils.getBeanMethods(type);
        final List<Field> fields = BeanUtils.getBeanField(type, Object.class);
        this.allFields = fields.toArray(Field[]::new);
        this.allMethods = allMethods.toArray(Method[]::new);
    }

    /**
     * 往类型池中添加一个bean
     *
     * @param instance
     */
    @Override
    public void setBean(Object instance) {
        if (!this.type.isInstance(instance)) {
            throw new RuntimeException(type.getSimpleName() + "与" + instance.getClass().getSimpleName() + "不同");
        }
        this.instance = instance;
    }

    /**
     * 获取该类型指向的bean
     *
     * @return
     */
    @Override
    public Object getBean() {
        return this.instance;
    }

    @Override
    public String getName() {
        return name;
    }

    @Override
    public void setName(String name) {
        this.name = name;
    }

    @Override
    public boolean isLaze() {
        return laze;
    }

    @Override
    public void setLaze(boolean laze) {
        this.laze = laze;
    }

    @Override
    public Class<?> getType() {
        return this.type;
    }

    /**
     * 设置bean构建工厂
     *
     * @param factory
     */
    @Contract("null->fail")
    @Override
    public void setFactory(FactoryBean factory) {
        Objects.requireNonNull(factory);
        this.factory = factory;
    }

    /**
     * 获取Bean工厂
     *
     * @return 构建这个bean的工厂
     */
    @Override
    public FactoryBean getFactory() {
        return factory;
    }

    /**
     * 判断这个bean是否有bean工厂
     *
     * @return true|false
     */
    @Override
    public boolean isFactory() {
        return factory != null;
    }

    @Override
    public Method[] getAllMethods() {
        return this.allMethods;
    }

    /**
     * 返回测试通过的method
     *
     * @param action
     */
    @Override
    public Method[] getMethodsIf(Predicate<Method> action) {
        final var objects = getCache(action);
        final Box<Method[]> box = Box.newEmptyBox();
        //这里是解析操作
        Runnable els = () -> {
            final var array = Arrays.stream(getAllMethods()).filter(action).toArray(Method[]::new);
            this.cache.put(action,array);
            box.set(array);
        };
        objects.ifPresentOrElse(
                (objs) -> {
                    if (objs[0].getClass().equals(Method.class)) {
                        box.set((Method[]) objs);
                    } else {
                        els.run();
                    }
                }, els);
        return box.get();
    }

    /**
     * 强制解析
     *
     * @param action
     * @param re
     */
    @Override
    public Method[] getMethodsIf(Predicate<Method> action, boolean re) {
        if (re){
            final var array = Arrays.stream(getAllMethods()).filter(action).toArray(Method[]::new);
            this.cache.put(action,array);
            return (Method[]) this.cache.get(action);
        }else {
            return getMethodsIf(action);
        }
    }

    @SuppressWarnings("rawtypes")
    private Optional<Object[]> getCache(Predicate action) {
        if (this.cache == null) {
            this.cache = new HashMap<>();
            return Optional.empty();
        } else {
            return Optional.ofNullable(this.cache.get(action));
        }
    }

    @Override
    public Field[] getAllField() {
        return this.allFields;
    }

    @Override
    public Constructor<?>[] getAllConstructor() {
        return this.allConstructor;
    }

    @Override
    public boolean isSingle() {
        return "singleton".equals(this.singleFlag);
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        BeanDef beanDef = (BeanDef) o;
        return getType().equals(beanDef.getType());
    }

    @Override
    public int hashCode() {
        return Objects.hash(getType());
    }
}
